<?php
namespace app\lib;

use think\Log;

/** 
* 工具类
* @author Tan <smallcatx0@gmail.com>
*/
class Tools {




    /**
     * 生成全局id
     *
     * @return void
     */
    public static function unID(){
        $startYear = 2018;
        $yCode = ['A','B','C','D','E','F','G','H','I','J','K','L','M'];
        $order_sn = '' 
            .$yCode[intval(date('Y')-$startYear)]   // 年(1)
            .\dechex(date('m'))                     // 月(1)
            .date('d')                              // 日(2)
            .sprintf('%02s',dechex(\rand(0,255)))  // 00-ff随机数(2)
            .\substr(\microtime(),2,5)              // 毫秒(5)
            .\substr(time(),-5)                     // 秒(5)
        ;
        return $order_sn;
    }


    /**
     * 利用reids生成流水号
     *
     * @param string $prefix 流水号前缀
     * @param int $numCount 自增流水号长度默认5
     * @author Tan <smallcatx0@gmail.com>
     * @return string|boolean
     */
    public static function buildDocID($prefix = '',$numCount= 5){
        $DocIDconfig = config('redis.DOC_ID');
        $tDate = date('Ymd');
        $rKey = 'DocID_'.$tDate;
        $hKey = 'DocID_'.$prefix;
        try{
            $redis = new \Redis();
            $redis->connect(config('redis.HOST'),config('redis.PORT'),config('redis.TIMEOUT'));
            $redis->select($DocIDconfig['database']); // 选择数据库
            $num = $redis->hIncrBy($rKey,$hKey,1); // 单据自增1
            // 设置过期时间
            if ($num == 1) $redis->expire($rKey,$DocIDconfig['timeout']);
        }catch(\Exception $e){
            Log::error($e->getMessage());
            return false;
        }
        return self::DocIDformat($prefix,$tDate,$num,$numCount);
    }
    
    public static function DocIDformat($pre,$date,$num,$len){
        return sprintf("%s%s%0{$len}d",$pre,$date,$num);
    }

    
    /**
     * 格式化xml数据
     *
     * @param array $array 待格式的数据，不包括根节点
     * @param string $rootName 根节点名
     * @param array $rootParam 根节点属性
     * @return string
     */
    public static function xml(array $data,$rootName='root',$rootParam=[]){
        header('Content-Type:text/xml');
        $xml = '<?xml version="1.0" encoding="UTF-8" ?>'."\n";
        $xmlRootParam = '';
        foreach ($rootParam as $key => $value) {
            $xmlRootParam .= " {$key}=\"{$value}\"";
        }
        $xml .= "<{$rootName}{$xmlRootParam}>";
        $xml .= self::xmlToEncode($data);
        $xml .= "</{$rootName}>";
        return $xml;
    }
    /**
     * 递归格式化Array->xml
     *
     * @param array $data
     * @return string
     */
    public static function xmlToEncode($data){
        $xml = $attr = '';
        foreach ($data as $key => $value) {
            if (is_numeric($key)) {
                $attr = ' id="' . $key . '"';
                $key = "item";
            }
            $xml .= "<{$key}{$attr}>";
            $xml .= is_array($value) ? self::xmlToEncode($value) : $value;
            $xml .= "</{$key}>";
        }
        return $xml;
    }

    /**
     * 将二维数组中某个字段作为KEY
     *
     * @param array $list 二维数组数据
     * @param string $field 选择做key的字段
     * @param boolean $delFiel 是否删除此字段
     * @author Tan <smallcatx0@gmail.com>
     * @return array
     */
    public static function listByKey($list,$field,$delFiel=false){
        $res = [];
        foreach ($list as $v) {
            $temp = $v;
            if ($delFiel) unset($temp[$field]);
            $res[$v[$field]] = $temp;
        }
        return $res;
    }
    /**
     * 删除两个列表中相同的元素，返回不同的元素
     *
     * @param array $l1 数组1
     * @param array $lOld 数组2
     * @param string $field 比较的依据字段
     * @param array $excl 排除、选择比较的字段
     * @param boolean $excl_reversa 默认第三个参数是排除比较，true则是选择比较
     * @author Tan <smallcatx0@gmail.com>
     * @return array
     */
    public static function listDiff(&$lNew,&$lOld,$field='ID',$excl=[],$excl_reversa = false){
        $lOld = self::listByKey($lOld,'ID');
        $diff = [];
        $diffData = [];
        foreach ($lNew as $k=>$v) {
            $new = $v;
            if (! isset($lOld[$v[$field]])) continue;
            $old = $lOld[$v[$field]];
            if ($excl_reversa) { // 只选择 $excl 里面的数据做比较
                foreach($excl as $v1){
                    $cNew[$v1] = $new[$v1];
                    $cOld[$v1] = $old[$v1];
                }
                $new = $cNew;
                $old = $cOld;
            }else{  // 排除$excl里面的数据做比较的字段
                foreach($excl as $exv){
                    unset($new[$exv]);
                    unset($old[$exv]);
                }
            }
            $diff = array_diff_assoc($new,$old); 
            if (empty($diff)) { // 元素相同,同时删掉
                unset($lNew[$k]);
                unset($lOld[$v[$field]]);
            }else{ // 元素不同，把新的放入返回值,同时删掉
                $diffData[] = $v;
                unset($lNew[$k]);
                unset($lOld[$v[$field]]);
            }
        }
        return $diffData;
    }
    /**
     * 向关联数组某键值前插入数据
     *
     * @param array $array 原始数组
     * @param array $data 待插入的数据
     * @param string $key 插入的数据的位置
     * @return array
     */
    public static function array_key_insert($array ,$data ,$key){
        $data  = (array)$data;
        $offset  = ($key===false) ? false : array_search($key, array_keys($array));
        $offset  = ($offset) ? $offset : false;
        if($offset){
            return array_merge(
                array_slice($array, 0, $offset),
                $data,
                array_slice($array, $offset)
            );
        }else{  // 没指定 $key 或者找不到，就直接加到末尾
            return array_merge($array, $data);
        }
    }

    /**
     * 两个数组联接（对标数据库Join操作）
     *
     * @param array $arr1
     * @param array $arr2
     * @param array $map 联接规则
     * @param string $type inner内联,left左联,right右联
     * @return array
     */
    public static function arrayJoin($arr1,$arr2,$map,$type='inner'){
        if($type == 'right') {
            $map = array_flip($map);
            return self::arrayJoin($arr2,$arr1,$map,'left');
        }
        $resData = [];
        foreach($arr1 as $v1){
            $med= 0;
            foreach($arr2 as $v2){
                if (self::toCompare($v1,$v2,$map)) {
                    $resData [] = array_merge($v1,$v2);
                    $med = 1;
                }
            }
            // 全都没匹配上?
            if($med == 0){
                if($type == 'left') $resData[] = $v1;
            }
        }
        return $resData;
    }
    /**
     * 判断两个元素是否可以联接
     *
     * @param array $v1
     * @param array $v2
     * @param array $map 联接规则
     * @return boolean
     */
    public static function toCompare($v1,$v2,$map){
        foreach ($map as $k=>$v) {
            $hasKeys = array_key_exists($k,$v1) && array_key_exists($v,$v2);
            if (! $hasKeys) {
                return false;
            }
            if ($v1[$k] != $v2[$v]){
                return false;
            }
        }
        return true;
    }

    /**
     * 二维数组字段克隆
     *
     * @param array $array 元数据
     * @param array $map 克隆规则 ['old_key'=>'new_key','new_key'] 
     * @return boolean
     */
    public static function listFieldClone(&$array,$map){
        $flage = true;
        foreach($array as &$v){
            foreach($map as $old=>$new){
                if(is_numeric($old)){
                    $v[$new] = '';
                }else{
                    if(isset($v[$old])) $v[$new] = $v[$old];
                    else $flage = false;
                }
            }
        }
        return $flage;
    }



    /**
     * 通过列表中某个字段的值拆分列表
     *
     * @param array $list 原始数据
     * @param string $model 匹配模式 value：值匹配,field：字段对比
     * @param array $condition 拆分条件 ['字段'=>'值'] or ['字段1'=>'字段2']
     * @author Tan <smallcatx0@gmail.com>
     * @return array
     */
    public static function listSplit(&$list,$model,$condition){
        $return = [];
        foreach ($list as $k => $v) {
            if(self::arrEligible($v,$model,$condition)){
                $return [] = $v;
                unset($list[$k]);
            }
        }
        return $return;
    }

    /**
     * 判断数据是否符合条件
     *
     * @param array $arr 数据源
     * @param string $model 匹配模式 value：值匹配 field 字段对比
     * @param array $condition 条件 ['字段'=>'值|字段2']
     * @author Tan <smallcatx0@gmail.com>
     * @return boolean
     */
    protected static function arrEligible($arr,$model, $condition){
        foreach ($condition as $field => $value) {
            if ($model == 'value') { // 值匹配模式
                if(isset($arr[$field]) && $arr[$field] == $value) continue;
                else return false;
            }elseif($model == 'field'){ // 字段对比模式
                if(isset($arr[$field]) && isset($arr[$value]) && $arr[$field]==$arr[$value]) continue;
                else return false;
            }
        }
        return true;
    }

    /**
     * 获取二维数组中的某些列
     *
     * @param array $list 源数据
     * @param array $field 需要截取的列 ['原列名'=>'新列名']
     * @param boolean $del 反转规则
     * @return void
     */
    public static function listColumns($list,$fields,$reverse=false){
        $res = [];
        foreach ($list as $v) {
            $res [] = self::itemColums($v,$fields,$reverse);
        }
        return $res;
    }

    /**
     * 筛选数组总的某些字段
     *
     * @param array $item 一条数据
     * @param array $fields 截取规则 ['原列名'=>'新列名']
     * @param boolean $reverse 是否反转规则 
     * @return void
     */
    protected static function itemColums($item,$fields,$reverse=false){

        $resItem = [];
        if ($reverse) {
            $resItem = $item;
            foreach ($fields as $v) {
                if (isset($resItem[$v])) unset($resItem[$v]);
            }
        }else{
            foreach ($fields as $old => $new) {
                if(is_numeric($old)) {
                    $resItem[$new] = isset($item[$new]) ? $item[$new] : '';
                }else{
                    $resItem[$new] = isset($item[$old]) ? $item[$old] : '';
                }
            }
        }
        return $resItem;
    }


    /**
     * 以二维数组中某行的键值分隔
     *
     * @param array $list 源数据
     * @param string $field 字段的值
     * @author Tan <smallcatx0@gmail.com>
     * @return array
     */
    public static function splitArr(array $list,$field){
        $resData = [];
        foreach ($list as $v) {
            $resData[$v[$field]][] = $v;
        }
        return $resData;
    }

}